package middleware

import (
	"fmt"
	"math"
	"os"
	"server/myutils"
	"strconv"
	"time"

	"github.com/gin-gonic/gin"
	retalog "github.com/lestrrat-go/file-rotatelogs"
	"github.com/rifflock/lfshook"
	"github.com/sirupsen/logrus"
)

//日志处理中间件
func Logger() gin.HandlerFunc {
	var (
		filePath = "log/"
		src, err = os.OpenFile(os.DevNull, os.O_CREATE|os.O_RDONLY|os.O_APPEND, os.ModePerm)
		logger   = logrus.New()
	)
	if err != nil {
		fmt.Println(err)
	}
	//文件存储位置
	logger.Out = src
	//debug级别（log.printf)以上的日志都会被存储
	logger.SetLevel(logrus.DebugLevel)
	//做日志分割
	loggerWriter, _ := retalog.New(
		filePath+"%Y%m%d.log",
		retalog.WithMaxAge(time.Duration(myutils.SurvivalTime)*time.Hour),
		retalog.WithRotationTime(24*time.Hour),
		retalog.WithLinkName("./log/Newlog.log"),
	)
	//规定什么级别做什么日志
	var writeMap = lfshook.WriterMap{
		logrus.DebugLevel: loggerWriter,
		logrus.ErrorLevel: loggerWriter,
		logrus.FatalLevel: loggerWriter,
		logrus.PanicLevel: loggerWriter,
		logrus.InfoLevel:  loggerWriter,
		logrus.WarnLevel:  loggerWriter,
	}
	//生成hook
	var hook = lfshook.NewHook(writeMap, &logrus.TextFormatter{
		TimestampFormat: "2006-01-02 15:04:05",
	})
	//hook塞入
	logger.AddHook(hook)
	//Note:代码写要从前往后写，读要从后往前读
	//middleware开始
	return func(ctx *gin.Context) {
		//执行时间
		var starttime = time.Now()
		//执行那
		ctx.Next()
		//获取执行时间
		var spendtime = int(math.Ceil(float64(time.Since(starttime).Nanoseconds()) / 1000000.0))
		var sspendtime = strconv.Itoa(spendtime) + "ms"
		//获取请求的ip地址
		hostname, _ := os.Hostname()
		//获取请求状态码
		var statuscode = ctx.Writer.Status()
		//客户端ip
		var clientip = ctx.ClientIP()
		//UA
		var useragent = ctx.Request.UserAgent()
		//FileSize
		var datasize = ctx.Writer.Size()
		if datasize < 0 {
			datasize = 0
		}
		//method PUT/GET/POST
		var method = ctx.Request.Method
		//path
		var path = ctx.Request.RequestURI

		//生成日志格式，给不同形式的日志打标记
		var entry = logger.WithFields(logrus.Fields{
			"Method":    method,
			"Path":      path,
			"Status":    statuscode,
			"SpendTime": sspendtime,
			"ClineIP":   clientip,
			"UA":        useragent,
			"HostName":  hostname,
		})
		//服务器内部错误记录处理
		if len(ctx.Errors) > 0 {
			//错误级别记录
			entry.Error(ctx.Errors.ByType(gin.ErrorTypePrivate).String())
			return
		}
		//返回状态码处理
		if statuscode >= 500 {
			entry.Error()
		} else if statuscode >= 400 {
			entry.Warn()
		} else {
			entry.Info()
		}

	}
}
